// Desenha caracteres na tela
#include <GL/glut.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <math.h>

#include "bolaclient.h"
#include "bola_phys.h"
#include "grafico.h"
#include "image.h"

#define DEG2RAD 0.0174533f      // Pi dividido por 180
#define RAD2DEG (180.0/PI)      // Pi dividido por 180
#define PI 3.14159
#define NUM_TEXTURAS 2

/** funções para o glut **/
static void display(void);

static void key(unsigned char key, int x, int y);

static void idle(void);

static void resize(int width, int height);

static void specialKey(int key, int x, int y);


/** Funções de desenho **/
static void DesenhaPiso();

static void DesenhaParedes();

static void DesenhaMesa();

static void DesenhaCacapa(float x, float y, float r);

static void DesenhaTaco(float x, float y, float ang);

static void DesenhaBolas(float r);

static void DesenhaTexto(char *string, float x, float y);

void carregarTexturas(void);

void iluminacao();

void saida(void);

static float angulo_taco = 0.0, ang_taco_oponente;

static float forca = 0.0;       // Forca da tacada (mais forte é 8000)

static int variando_forca = 0;

static int anim_tacada = 0;		// aciona a animação de tacada

static int estado = 0;          // estado do jogo (0 - jogar; 1 - esperar o adversario)

static int sala, num_tacada, estado_bola[9] = { }, time, vencedor = 0;

static int vx = 1, vy = 0;

static int camera = 1;			//estado da camera (1 - camera segue o taco; 0 - camera por cima)

static char name[80], nome[80], nome_sala[80], place[80];

static player *p;

static GLuint imagem[NUM_TEXTURAS];

static GLUquadricObj *quadrica;





void game_init()
{
    /* sala = num; estado = e; time = e; */
    p = malloc(sizeof(player));

    int argc = 1;
    char *argv[] = { "Bola8" };
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Bola 8");

    /* Capacidades do OpenGL */
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_FLAT);

    glClearColor(1.0, 1.0, 1.0, 0.0);
    glPointSize(3.0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-800.0f, 800.0f, -600.0, 600.0, -6000.0, 6000.0);

	quadrica = gluNewQuadric(); //para criar o cilindro (taco)

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // callbacks
    glutDisplayFunc(display);
    glutKeyboardFunc(key);
    glutSpecialFunc(specialKey);
    glutIdleFunc(idle);
    atexit(saida);

    carregarTexturas();

    glutMainLoop();
}

// Função que é chamada quando for preciso mostrar ou atualizar a tela
static void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    if (vx == 1)
    {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glColor3d(255, 255, 0);
        DesenhaTexto("INSIRA SEU NOME:", -200.0f, 100.0f);

        glColor3d(255, 0, 0);
        sprintf(name, nome);
        DesenhaTexto(name, -200.0f, -100.0f);
    }

    if (vx == 2)
    {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        if (vy == 0)
        {
            glColor3d(255, 0, 0);
            DesenhaTexto("CRIAR SALA", -150.0f, 70.0f);

            glColor3d(255, 255, 0);
            DesenhaTexto("ACESSAR SALA", -150.0f, -70.0f);
        }

        if (vy == 1)
        {
            glColor3d(255, 255, 0);
            DesenhaTexto("CRIAR SALA", -150.0f, 70.0f);


            glColor3d(255, 0, 0);
            DesenhaTexto("ACESSAR SALA", -150.0f, -70.0f);
        }
    }

    if (vx == 3)
    {

        if (vy == 0)
        {
            glColor3d(255, 255, 0);
            DesenhaTexto("INSIRA O NOME DA SALA:", -200.0f, 100.0f);

            glColor3d(255, 0, 0);
            sprintf(nome_sala, place);
            DesenhaTexto(nome_sala, -200.0f, -100.0f);
        }

        if (vy == 1)
        {
            glColor3d(255, 255, 0);
            DesenhaTexto("INSIRA O NUMERO DA SALA", -200.0f, 100.0f);

            glColor3d(255, 0, 0);
            sprintf(nome_sala, place);
            DesenhaTexto(nome_sala, -100.0f, -100.0f);
        }
    }

    if (vx == 4)
    {
        glColor3d(255, 255, 255);
        DesenhaTexto("JOGAR!", -30.0f, 0.0f);
        phys_init();
    }

    if (vx >= 5)
    {
        glClearColor(255.0f, 255.0f, 255.0f, 1.0f);

		/** HUD **/
		glPushMatrix();

        glColor3f(1000, 1000, 1000);
        char mostra_num_sala[20];

        sprintf(mostra_num_sala, "%d", sala);
        DesenhaTexto("sala: ", 50.0, 520.0);
        DesenhaTexto(mostra_num_sala, 150.0, 520.0);

		//Desenha a barra de forca
		if (variando_forca)
		{
			glPushMatrix();
				glBegin(GL_QUADS);
					glColor3f(1.0, 0.1, 0.1);
					glNormal3f(0, 0, 1);
					glVertex3f(-600 +  0.15*forca, -500, 2500);
					glVertex3f(-600 +  0.15*forca, -400, 2500);
					glVertex3f(-600, -400, 2500);
					glVertex3f(-600, -500, 2500);
				glEnd();
			glPopMatrix();
		}

		glPopMatrix();
		/********/

        iluminacao();

		/** controle da câmera **/
		glPushMatrix();

		if (camera == 1) {
			glRotatef(-70, 1, 0, 0);
			glRotatef(-angulo_taco*RAD2DEG - 90, 0, 0, 1);
			glScalef(2.5,2.5,2.5);
			float x, y;
    	    ball_get_pos(&x, &y, 8); //pega pos da bola branca
			glTranslatef(-x, -y, 0);
		}


        // Desenha a Mesa de Sinuca
        glColor3d(255, 255, 0);
        DesenhaMesa();
	DesenhaPiso();
	DesenhaParedes();
        DesenhaCacapa(-200.0, -200.0, 15.0);
        DesenhaCacapa(100.0, -200.0, 15.0);
        DesenhaCacapa(400.0, -200.0, 15.0);
        DesenhaCacapa(400.0, 200.0, 15.0);
        DesenhaCacapa(100.0, 200.0, 15.0);
        DesenhaCacapa(-200.0, 200.0, 15.0);
        DesenhaBolas(10.0);

        // Checa se nenhuma bola foi encaçapada
        int i;

        for (i = 0; i < 9; i++)
        {
            if (ball_destroyed(i))
            {
                if (i == 8)
                {
                    ball_set_pos(220.0, 0.0, 8);
                    ball_stop(8);
                }
                else
                {
                    ball_set_pos(5000.0 + 20.0 * i, 5000.0, i);
					ball_stop(i);
                    estado_bola[i] = 1;
                }
            }
        }
        // Checa se o jogo já acabou
        int cont = 0, aux_time;

        for (i = 4 * time; i < 4 * time + 5; i++)
        {
            cont += ball_destroyed(i);
        }
        if (cont == 4)
        {
            vencedor = 1;
        }
        aux_time = (time == 0) ? 1 : 0;
        cont = 0;
        for (i = 4 * aux_time; i < 4 * aux_time + 5; i++)
        {
            cont += ball_destroyed(i);
        }
        if (cont == 4)
        {
            vencedor = 2;
        }

        // Desenha Taco do Jogador
        if (estado == 0 && balls_movement() == 0.0)
        {
            float x, y;

            ball_get_pos(&x, &y, 8);
            glColor3d(255, 255, 255);
            DesenhaTaco(x, y, angulo_taco);
        }

		//Esperando tacada do adversário
        if (estado == 1)
        {
            if (waitshot(num_tacada, sala) == 1)
            {
                forca = waitshotforce(sala);
                ang_taco_oponente = waitshotangle(sala);
                ball_apply_impulse(forca, ang_taco_oponente, 8);
                estado = 0;
            }
        }

		//Finalização do jogo
        if (vencedor != 0)
        {
            // TODO finaliza o jogo
            vx = 2;
        }

		glPopMatrix();

        forca += variando_forca * 120;
        if (forca >= 8000.0 || (forca <= 0 && variando_forca < 0))
            variando_forca = -variando_forca;


        phys_update();
    }

    glutSwapBuffers();
}

static void key(unsigned char key, int x, int y)
{

    if (vx < 5)
    {
        switch (key)
        {
        case 'q':
            {
                // Sai do programa quando o código ASCII for 'q'
                exit(0);
                break;
            }

        case 27:
            {
                // Volta para a tela anterior
                vx -= 1;
                break;
            }

        case 13:
            {
                if (vx == 1)
                {
                    // adicionar jogador
                    p->name = name;
                    if (addplayer(p) == -1)
                    {
                        printf("Erro ao criar jogador.\n");
                        exit(1);
                    }
                }
                if (vx == 3)
                {
                    if (vy == 0)
                    {
                        // criar sala
                        if ((sala = createroom(nome_sala, p)) == -1)
                        {
                            printf("Não ha espaco no servidor para criar essa sala. Tente mais tarde.\n");
                            exit(1);
                        }
                        estado = 1;
                        time = 1;
                    }
                    else
                    {
                        // acessar sala
                        sala = atoi(nome_sala);
                        if (joinroom(sala, p) == -1)
                        {
                            printf("Sala ocupada ou inexistente!\n");
                            exit(1);
                        }
                        estado = 0;
                        time = 0;
                    }
                }

                // Passa para a próxima tela
                vx += 1;
                break;
            }
        }

        if (vx == 3)
        {
            char str[2] = { };
            str[0] = key;
            strcat(place, str);
        }

        char str[2] = { };
        str[0] = key;
        strcat(nome, str);
    }
    else
    {
        if (key == 32)
        {
            if (estado == 0 && balls_movement() == 0.0) 
			{
				if (variando_forca == 0) 
				{
					forca = 0;
					variando_forca = 1;
				}
				else
				{
					anim_tacada = 1; //Aciona a animação de tacada TODO
                    /*estado = 1;
                    num_tacada = makeshot(forca, angulo_taco + PI, sala);
                    if (num_tacada == -1)
                    {
                        printf("O outro jogador deixou a sala.\n");
                        exit(1);
                    }
                    ball_apply_impulse(forca, angulo_taco + PI, 8);
                    forca = 0;*/
					variando_forca = 0;
				}
			}
        }
		else if (key == 9)
		{
			camera = (camera == 1) ? 0 : 1;
		}
    }
}

static void specialKey(int key, int x, int y)
{
    if (vx < 5)
    {

        switch (key)
        {
        case GLUT_KEY_UP:      // Caso direcional para CIMA - Limpa vy
            {
                vy = 0;
                break;
            }

        case GLUT_KEY_DOWN:    // Caso direcional para BAIXO - Seta vy
            {
                vy = 1;
                break;
            }
        }

    }
    else
    {

        switch (key)
        {
        case GLUT_KEY_LEFT:
        case GLUT_KEY_UP:
            angulo_taco += 0.03;
            break;
        case GLUT_KEY_RIGHT:
        case GLUT_KEY_DOWN:
            angulo_taco -= 0.03;
            break;
        }

    }
}

// Função que executa rotinas padrões da Glut
static void idle(void)
{
    glutPostRedisplay();
}

static void DesenhaPiso()
{
    /*Desenha o Piso da Sala de Jogo*/
    //glEnable(GL_TEXTURE_2D); 
    glBindTexture(GL_TEXTURE_2D, imagem[0]);  
    glColor3f(1.0f, 1.0f, 1.0f);	
    glPushMatrix();
    glBegin(GL_QUADS); 
    glNormal3f(0, 0, 1);
    glTexCoord2f(0.0f,0.0f); glVertex3f(-1000.0f, -1000.0f, -401.0f); 
    glTexCoord2f(1.0f,0.0f); glVertex3f( 1000.0f, -1000.0f, -401.0f); 
    glTexCoord2f(1.0f,1.0f); glVertex3f( 1000.0f, 1000.0f, -401.0f); 
    glTexCoord2f(0.0f,1.0f); glVertex3f(-1000.0f, 1000.0f, -401.0f); 
    glEnd();
    glPopMatrix();
}

static void DesenhaParedes()
{
        glColor3f(1.0f, 1.0f, 1.0f);	
	glPushMatrix();
	glBindTexture(GL_TEXTURE_2D, imagem[1]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,0.0f); glVertex3f(-1000.0, -1000.0, -401.0);
        glTexCoord2f(1.0f,0.0f); glVertex3f(1000.0, -1000.0, -401.0);
        glTexCoord2f(1.0f,1.0f); glVertex3f(1000.0, -1000.0, 20.0);
        glTexCoord2f(0.0f,1.0f); glVertex3f(-1000.0, -1000.0, 20.0);
	glEnd();
	glPopMatrix();
	
	glPushMatrix();
	glBindTexture(GL_TEXTURE_2D, imagem[1]);	
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,0.0f); glVertex3f(-1000.0, -1000.0, -401.0);
        glTexCoord2f(1.0f,0.0f); glVertex3f(-1000.0, 1000.0, -401.0);
        glTexCoord2f(1.0f,1.0f); glVertex3f(-1000.0, 1000.0, 20.0);
        glTexCoord2f(0.0f,1.0f); glVertex3f(-1000.0, -1000.0, 20.0);
	glEnd();
	glPopMatrix();

	glPushMatrix();	
	glBindTexture(GL_TEXTURE_2D, imagem[1]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,0.0f); glVertex3f(1000.0, -1000.0, -401.0);
        glTexCoord2f(1.0f,0.0f); glVertex3f(1000.0, 1000.0, -401.0);
        glTexCoord2f(1.0f,1.0f); glVertex3f(1000.0, 1000.0, 20.0);
        glTexCoord2f(0.0f,1.0f); glVertex3f(1000.0, -1000.0, 20.0);
	glEnd();
	glPopMatrix();

	glPushMatrix();
	glBindTexture(GL_TEXTURE_2D, imagem[1]);
	glBegin(GL_QUADS);
	glTexCoord2f(0.0f,0.0f); glVertex3f(-1000.0, 1000.0, -401.0);
        glTexCoord2f(1.0f,0.0f); glVertex3f(1000.0, 1000.0, -401.0);
        glTexCoord2f(1.0f,1.0f); glVertex3f(1000.0, 1000.0, 20.0);
        glTexCoord2f(0.0f,1.0f); glVertex3f(-1000.0, 1000.0, 20.0);
	glEnd();
	glPopMatrix();
}

static void DesenhaMesa()
{
    /* Mesa marrom */
    glColor3f(0.8f, 0.65f, 0.3f);
    glPushMatrix();
    glTranslatef(100.0, 0, -100.1);
    glScalef(640.0, 440.0, 200.0);
    glutSolidCube(1.0);
    glPopMatrix();

    /* pernas da mesa */
    glColor3f(0.0f, 0.0f, 0.0f);
    glPushMatrix();
    glTranslatef(360.0, -160, -300);
    glScalef(30.0, 30.0, 200.0);
    glutSolidCube(1.0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(360.0, 160, -300);
    glScalef(30.0, 30.0, 200.0);
    glutSolidCube(1.0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(-140.0, 160, -300);
    glScalef(30.0, 30.0, 200.0);
    glutSolidCube(1.0);
    glPopMatrix();

    glPushMatrix();
    glTranslatef(-140.0, -160, -300);
    glScalef(30.0, 30.0, 200.0);
    glutSolidCube(1.0);
    glPopMatrix();

/*
    glEnable(GL_TEXTURE_2D); 
    glBindTexture ( GL_TEXTURE_2D,imagem );
    /* Parte verde /
    glColor3f(0.2f, 0.85f, 0.0f);
    glPushMatrix();
    glTranslatef(100.0, 0, 0);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f,0.0f);glVertex3f(300.0f, -200.0f, 0);
    glTexCoord2f(1.0f,0.0f);glVertex3f(300.0f, 200.0f, 0);
    glTexCoord2f(1.0f,1.0f);glVertex3f(-300.0f, 200.0f, 0);
    glTexCoord2f(0.0f,1.0f);glVertex3f(-300.0f, -200.0f, 0);
    glEnd();
    glPopMatrix(); */

    /* Parte verde */
    glColor3f(0.2f, 0.85f, 0.0f);
    glPushMatrix();
    glTranslatef(100.0, 0, 0);
    glBegin(GL_QUADS);
		glNormal3f(0, 0, 1);
    	glVertex3f(300.0f, -200.0f, 0);
	    glVertex3f(300.0f, 200.0f, 0);
    	glVertex3f(-300.0f, 200.0f, 0);
	    glVertex3f(-300.0f, -200.0f, 0);
    glEnd();
    glPopMatrix();

}

static void DesenhaCacapa(float x, float y, float r)
{
    int i;

    float alfa;

    glColor3d(0, 0, 0);

    glBegin(GL_POLYGON);

    for (i = 0; i < 360; i++)
    {
        alfa = i * DEG2RAD;
        glVertex3f(x + cos(alfa) * r, y + sin(alfa) * r, 0.2);
    }
    glEnd();
}

static void DesenhaBolas(float r)
{
    int i, j;

    float alfa;

    float x, y;

    /* Bolas vermelhas */
    glColor3d(255, 0, 0);

    for (j = 0; j < 4; j++)
    {
        ball_get_pos(&x, &y, j);

        glPushMatrix();
        glTranslatef(x, y, r);
        glutSolidSphere(r, 50, 50);
        glPopMatrix();
    }

    /* Bolas azuis */
    glColor3d(0, 0, 255);
    for (j = 4; j < 8; j++)
    {
        ball_get_pos(&x, &y, j);

        glPushMatrix();
        glTranslatef(x, y, r);
        glutSolidSphere(r, 50, 50);
        glPopMatrix();
    }

    /* Bola branca */
    glColor3d(255, 255, 255);

    ball_get_pos(&x, &y, 8);

    glPushMatrix();
    glTranslatef(x, y, r);
    glutSolidSphere(r, 50, 50);
    glPopMatrix();
}

// usa uma posição (x,y), o comprimento l, a largura w e o ângulo como base 
// 
// 
static void DesenhaTaco(float x, float y, float ang)
{
    const float w = 10.0; 	//Espessura da base do taco
	const float l =  300.0; //Tamanho do taco
	const float z = 10.0;
	const float theta = 15;

    glColor3f(0.7f, 0.4f, 0.0f);

	glPushMatrix();

	if (anim_tacada) {
		static float passo = 0.1;
		passo += 0.2;

		glTranslatef(cos(ang)*sin(passo)*forca/50,
					 sin(ang)*sin(passo)*forca/50,
					 /*-sin(theta)*/0);

		if (passo > 3.0) 
		{
			anim_tacada = 0;
			passo = 0.1;
			/* Realiza a tacada */
            estado = 1;
            num_tacada = makeshot(forca, angulo_taco + PI, sala);
            if (num_tacada == -1)
            {
                printf("O outro jogador deixou a sala.\n");
                exit(1);
            }
            ball_apply_impulse(forca, angulo_taco + PI, 8);
            forca = 0;
		}
	}
	glTranslatef(x, y, z);
	glRotatef(ang*RAD2DEG, 0, 0, 1);
	glRotatef(-theta, 0, 1, 0);
	glTranslatef(l + 15.0, 0, 0);
	glRotatef(-90, 0, 1, 0);
	gluCylinder(quadrica, w, w/2.0, l, 32, 32);

	glPopMatrix();
}

void DesenhaTexto(char *string, float x, float y)
{
    glPushMatrix();
    // Posição no universo onde o texto será colocado
    glRasterPos2f(x, y);
    // Exibe caracter a caracter
    while (*string)
    {
        glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, *string++);
    }
    glPopMatrix();
}

void carregarTexturas()
{
    IMAGE *img;

    GLenum gluerr;


    glEnable(GL_TEXTURE_2D);
    // Imagem de abertura
    glGenTextures(1, &imagem);

    
	glBindTexture(GL_TEXTURE_2D, imagem[0]);        
	if (!(img = ImageLoad("Textura0.rgb")))
        {
            fprintf(stderr, "Error reading a texture.\n");
            exit(-1);
        }

        gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
                               img->sizeX, img->sizeY,
                               GL_RGB, GL_UNSIGNED_BYTE,
                               (GLvoid *) (img->data));
        if (gluerr)
        {
            fprintf(stderr, "GLULib%s\n", gluErrorString(gluerr));
            exit(-1);
        }

glBindTexture(GL_TEXTURE_2D, imagem[1]);        
	if (!(img = ImageLoad("Textura1.rgb")))
        {
            fprintf(stderr, "Error reading a texture.\n");
            exit(-1);
        }

        gluerr = gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
                               img->sizeX, img->sizeY,
                               GL_RGB, GL_UNSIGNED_BYTE,
                               (GLvoid *) (img->data));
        if (gluerr)
        {
            fprintf(stderr, "GLULib%s\n", gluErrorString(gluerr));
            exit(-1);
        }

    //}

    //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    //glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
    glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE, GL_MODULATE); 

}

void iluminacao()
{
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_COLOR_MATERIAL);

    float luzAmbiente[4] = { 0.05f, 0.05f, 0.05f, 1.0f };
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, luzAmbiente);

    //float especularidade[4] = { 0.2f, 0.2f, 0.2f, 0.0f };
    //glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, especularidade);

    //float especmaterial = 2;

    float difusa[4] = { 0.1f, 0.1f, 0.1f, 1.0f };
    glMaterialfv(GL_BACK, GL_DIFFUSE, difusa);



glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    //glMateriali(GL_FRONT, GL_SHININESS, especmaterial);

    GLfloat posicaoLuz[4] = { 0.0f, 0.0f, 500.0f, 1.0f };
    GLfloat luzDifusa[4] = { 0.5f, 0.5f, 0.5f, 1.0f };		// sombreado
    GLfloat luzEspecular[4] = { 0.1f, 0.1f, 0.1f, 1.0f };   // brilho

    glLightfv(GL_LIGHT0, GL_DIFFUSE, luzDifusa);
    glLightfv(GL_LIGHT0, GL_SPECULAR, luzEspecular);
    glLightfv(GL_LIGHT0, GL_AMBIENT, luzAmbiente);
    glLightfv(GL_LIGHT0, GL_POSITION, posicaoLuz);
}

void saida()
{
    rmroom(sala);
}
